const std = @import("std");
const File = @This();
fn Iterator(is_base_iter: bool, T: type, Context: type, ArgsType: type, Operation: type) type {
    return struct {
        context: Context,
        inext: if (is_base_iter) *const fn (*Context) ?T else *const fn (*Context, ArgsType, Operation) ?T,
        args: ArgsType,
        operation: Operation,

        const Self = @This();

        pub inline fn next(self: *Self) ?T {
            return if (is_base_iter) self.inext(&self.context) else self.inext(&self.context, self.args, self.operation);
        }

        pub fn filter(self: Self, other_args: anytype, func: *const fn (T, @TypeOf(other_args)) bool) Iterator(false, T, Self, @TypeOf(other_args), @TypeOf(func)) {
            const nx = struct {
                fn inext(context: *Self, args: @TypeOf(other_args), operation: @TypeOf(func)) ?T {
                    while (context.next()) |value| {
                        if (operation(value, args)) {
                            return value;
                        }
                    }
                    return null;
                }
            }.inext;
            return .{ .context = self, .inext = nx, .args = other_args, .operation = func };
        }

        pub fn map(self: Self, U: type, other_args: anytype, func: *const fn (T, @TypeOf(other_args)) U) Iterator(false, U, Self, @TypeOf(other_args), @TypeOf(func)) {
            const nx = struct {
                fn inext(context: *Self, args: @TypeOf(other_args), operation: @TypeOf(func)) ?U {
                    return if (context.next()) |value| operation(value, args) else null;
                }
            }.inext;
            return .{ .context = self, .inext = nx, .args = other_args, .operation = func };
        }

        pub inline fn getIndex(self: @This()) usize {
            return self.context.getIndex();
        }
        pub inline fn eqlIndex(self: @This(), other: @TypeOf(self)) bool {
            return self.context.getIndex() == other.context.getIndex();
        }

        pub fn toHeap(self: @This(), allocator: std.mem.Allocator) !*@This() { // <><>
            return File.toHeap(allocator, self); // <+><+>
        }
    };
}

pub fn init(T0: type, context: anytype, inext: *const fn (*@TypeOf(context)) ?T0) Iterator(true, T0, @TypeOf(context), void, void) {
    return .{ .args = {}, .context = context, .inext = inext, .operation = {} };
}

fn Slice(Slice_T: type) type {
    return struct {
        slice_1: Slice_T,
        index: usize,
        pub fn getIndex(self: @This()) usize {
            return self.index;
        }
    };
}
pub fn fromSliceConst(T: type, slice: []const T) Iterator(true, T, Slice(@TypeOf(slice)), void, void) {
    const slice_it: Slice(@TypeOf(slice)) = .{
        .slice_1 = slice,
        .index = 0,
    };
    const inext: *const fn (*Slice(@TypeOf(slice))) ?T = struct {
        pub fn func(slice_2: *Slice(@TypeOf(slice))) ?T {
            if (slice_2.index == slice_2.slice_1.len) return null;
            defer slice_2.index += 1;
            return slice_2.slice_1[slice_2.index];
        }
    }.func;
    return .{ .context = slice_it, .inext = inext, .args = {}, .operation = {} };
}
pub fn fromSlicePtr(T: type, slice: []T) Iterator(true, *T, Slice(@TypeOf(slice)), void, void) {
    const slice_it: Slice(@TypeOf(slice)) = .{
        .slice_1 = slice,
        .index = 0,
    };
    const inext: *const fn (*Slice(@TypeOf(slice))) ?*T = struct {
        pub fn func(slice_2: *Slice(@TypeOf(slice))) ?*T {
            if (slice_2.index == slice_2.slice_1.len) return null;
            defer slice_2.index += 1;
            return &slice_2.slice_1[slice_2.index];
        }
    }.func;
    return .{ .context = slice_it, .inext = inext, .args = {}, .operation = {} };
}

pub inline fn toHeap(allocator: std.mem.Allocator, value: anytype) !*@TypeOf(value) { // <><>
    const ptr = try allocator.create(@TypeOf(value)); // <+><+>
    errdefer allocator.free(ptr);
    ptr.* = value;
    return ptr;
}
